﻿/// <reference path="jquery.js" />
/// <reference path="svg.js />
/// <reference path="jquery.colorbox.js />


var db = {
    totalCountsNum: 18,
    offset: { left: 0, top: 0 },
    size: { w: 800, h: 600 },
    path: [{ x: 0, y: 300 }, { x: 1100, y: 300}],
    isTimeout: false,
    isInBoundary: function (x, y) {
        return y <= 600;
    },
    isEnded: false,
    gameEnded: function (win) {
        if (!db.isEnded) {
            db.isEnded = true;
            $('#counter').trigger("terminate");
            $("[db-cat='ball']").trigger("active", false);
            db.pause = true;

            var html = "";
            if (win) {
                var rewards = $("#main [db-cat='reward']").length;
                html += "<h1>Got it!</h1><img style='height:155px; width:125px; margin-left:107px' src='../img/win.jpg'/><br/>";
                for (var i = 0; i < 3 - rewards; i++)
                    html += "<img style='border:0px;height:50px; width:50px; margin-left:43px; margin-top:20px' src='../img/star.png'/>";
                for (i = 0; i < rewards; i++)
                    html += "<img style='border:0px;height:50px; width:50px; margin-left:43px; margin-top:20px' src='../img/stargray.png'/>";
                sound.once("win.mp3");
            }
            else {
                html = "<h1>Game Over</h1><br/><img style='border:0px;height:170px; width:225px; margin-left:50px' src='../img/gameover.png'/>";
                sound.once("whistle.mp3");
            }
            html += db.getPopControls({ showNext: win });

            setTimeout(function () {
                db.pause = false;
                $.colorbox({ width: 400, height: 500, overlayClose: false, onClosed: function () { location.reload(); }, html: html });
            }, 500);
        }
    },
    pause: false,
    setTimer: function (sec, func, termFunc) {
        if (termFunc == null)
            termFunc = function () { };

        function recursiveFunction(time, func) {
            if (!db.pause) {
                if (time > 0 || time === null) {
                    var terminate = func();
                    if (terminate) {
                        termFunc();
                        return;
                    }

                    if (time)
                        time--;
                    setTimeout(function () { recursiveFunction(time, func); }, 20);
                }
                else {
                    termFunc();
                }
            }
            else
                setTimeout(function () { recursiveFunction(time, func); }, 20);
        }

        if (sec === null)
            recursiveFunction(null, func);
        else
            recursiveFunction(sec * 50, func);
    },
    previewCanceled: false,
    previewMap: function (completed) {
        if (completed == undefined)
            completed = function () { };

        var l = db.path.length;
        var i = l - 1;
        var cameraPosi = db.path[i];
        var currectScorll = null;

        $("#win").scrollLeft(cameraPosi.x - db.size.w / 2);
        $("#win").scrollTop(cameraPosi.y - db.size.h / 2);

        var v = { x: 0, y: 0 };
        var pointChanged = true;

        setTimeout(function () {
            db.setTimer(null, function () {
                return setCamera();

            }, function () {
                completed();
            });
        }, 500);

        function setCamera() {
            if (db.previewCanceled) {
                $("#win").scrollLeft(0);
                $("#win").scrollTop(0);
                return true;
            }
            else {
                if (pointChanged) {
                    if (i > 0) {
                        var diff = geo.vectorDiff(db.path[i - 1], db.path[i]);
                        v = geo.vectorNorm(diff);

                    } else {
                        return true;
                    }
                }

                cameraPosi = geo.vectorAdd(cameraPosi, geo.vectorProcuctNum(v, 3));
                if (geo.vectorDot(v, geo.vectorDiff(db.path[i - 1], cameraPosi)) < 0) {
                    pointChanged = true;
                    i--;
                }
                else {
                    pointChanged = false;

                    $("#win").scrollLeft(cameraPosi.x - db.size.w / 2);
                    $("#win").scrollTop(cameraPosi.y - db.size.h / 2);
                    var scroll = { x: $("#win").scrollLeft(), y: $("#win").scrollTop() };
                    if (currectScorll)
                        if (currectScorll.x == scroll.x && currectScorll.y == scroll.y)
                            return setCamera();

                    currectScorll = scroll;
                }
            }

            return false;
        }
    },
    getPopControls: function (opts) {
        opts = $.extend({
            showNext: false,
            upLink: "Index.html"
        }, opts || {});
        var url = window.location.href;
        var html = "<div id='bottons'><a href='" + opts.upLink + "'><img src='../img/up.jpg'/></a><a href='" + url + "'><img src='../img/re.jpg'/></a>";
        if (opts.showNext) {
            var dot = url.lastIndexOf(".xhtml");
            var slash = url.lastIndexOf("/");
            var current = parseFloat(url.substring(slash + 1, dot));
            if (current < db.totalCountsNum) {

                var nextUrl = url.substring(0, slash + 1) + (current + 1) + ".xhtml";
                html += "<a href='" + nextUrl + "'><img src='../img/next.jpg'/></a>"
            }
        }

        html += "</div>";
        return html;
    },
    inDark: false,
    lightSources: [],
    setLights: function (id, radius, position) {
        if (db.inDark) {
            var l = db.lightSources.length;
            var i = 0;
            for (; i < l; i++) {
                if (db.lightSources[i].id == id) {
                    db.lightSources[i].radius = radius;
                    db.lightSources[i].position = position;
                    break;
                }
            }

            if (i >= l) {
                db.lightSources.push({ id: id, radius: radius, position: position });
            }
            var path = "";
            for (i = 0; i < l; i++) {
                path += "M " +
                    db.lightSources[i].position.x + " " + (db.lightSources[i].position.y - db.lightSources[i].radius) +
                    "a " + db.lightSources[i].radius + " " + db.lightSources[i].radius + " 0 1 0 0.01 0";
            }
            path += "z";

            $("#lightPath").attr("d", path);
            $("#lightShadow").attr("d", path);
        }
    }
};
db.__namespace = true;

var triggers = {
    clicked: function (e) { },
    ballMoved: function (e) { },
    gameStarted: function (e) { },
    getReward: function (e) { },
    hitButton: function (e) { },
    hitToggle: function (e) { },
    padRemoved: function (e) { }
};
triggers.__namespace = true;

var camera = {
    set: function (x, y, moved) {
        if (moved) {
            function minMax(a, b) {
                if (a < b)
                    return [a, b];
                else
                    return [b, a];
            }
            var range = [];
            var l = db.path.length;
            for (var i = 0; i < l - 1; i++) {
                var p1 = db.path[i];
                var p2 = db.path[i + 1];

                var xs = minMax(p1.x, p2.x);
                var ys = minMax(p1.y, p2.y);

                if (x >= xs[0] && x <= xs[1] || y >= ys[0] && y <= ys[1]) {
                    range.push({ p1: p1, p2: p2 });
                }
            }
            if (range.length > 0) {
                l = range.length;
                var dis = Infinity;
                var targetPath = null;
                for (i = 0; i < l; i++) {
                    var d = geo.distP2Line(range[i].p1, range[i].p2, { x: x, y: y });
                    if (d < dis) {
                        dis = d;
                        targetPath = range[i];
                    }
                }

                camera.targetPoint = geo.p2lineProject({ x: x, y: y }, targetPath.p1, targetPath.p2);
                var moveDirection = geo.vectorNorm({ x: targetPath.p2.x - targetPath.p1.x, y: targetPath.p2.y - targetPath.p1.y });

                camera.targetPoint.x += moveDirection.x * db.size.w * camera.centerOffset - db.size.w / 2;
                camera.targetPoint.y += moveDirection.y * db.size.h * camera.centerOffset - db.size.h / 2;

                //change path
                if (!camera.currentPath || (targetPath.p1.x != camera.currentPath.p1.x || targetPath.p1.y != camera.currentPath.p1.y ||
                    targetPath.p2.x != camera.currentPath.p2.x || targetPath.p2.y != camera.currentPath.p2.y)) {
                    camera.ticks = 0;
                    camera.currentPath = targetPath;
                }
            }
        }
        if (camera.targetPoint) {
            if (camera.ticks > 100) {
                $("#win").scrollLeft(camera.targetPoint.x);
                $("#win").scrollTop(camera.targetPoint.y);
            }
            else {
                var carmeraPosi = {
                    x: $("#win").scrollLeft(),
                    y: $("#win").scrollTop()
                };

                var carmeraMoveDir = geo.vectorAdd(carmeraPosi, geo.vectorProcuctNum(geo.vectorDiff(camera.targetPoint, carmeraPosi), camera.ticks / 100));
                $("#win").scrollLeft(carmeraMoveDir.x);
                $("#win").scrollTop(carmeraMoveDir.y);

                camera.ticks++;
            }
        }

    },
    ticks: 0,
    currentPath: null,
    targetPoint: null,
    centerOffset: 0.25
};
camera.__namespace = true;

function startGame(opts) {
    opts = $.extend({
        time: 24,
        ballPosition: { x: 100, y: 100 },
        timeout: function () { },
        addCeilingBorder: true
    }, opts);

    $("#status").hover(function () {
        $(this).hide();
    }, function () {
        var div = $(this);
        setTimeout(function () { div.fadeIn(); }, 1000);
    });

    $("#skip").click(function () {
        db.previewCanceled = true;
    });

    initMap({ addCeilingBorder: opts.addCeilingBorder });

    function url_query(query) {
        query = query.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
        var expr = "[\\?&]" + query + "=([^&#]*)";
        var regex = new RegExp(expr);
        var results = regex.exec(window.location.href);
        if (results !== null) {
            return results[1];
        } else {
            return false;
        }
    }

    if (url_query('p')) {
        function preview() {
            db.previewMap(function () {
                preview();
            });
        }
        $("#skip").hide();
        preview();
        $('body').css("cursor", "pointer");
        $('body').click(function () {
            parent.location = window.location.href.substring(0, window.location.href.length - 4);
        });

        $('body').bind('touchstart', function () { parent.location = window.location.href.substring(0, window.location.href.length - 4); });
    }
    else {
        db.previewMap(function () {
            $("#skip").hide();
            var ball = new Ball();
            ball.init(opts.ballPosition.x, opts.ballPosition.y);

            $('#counter').countdown({
                startTime: opts.time.toString(),
                timerEnd: function () {
                    db.isTimeout = true;
                    sound.once("timeout.mp3");
                    $("[db-cat='ball']").trigger("active", false);
                    opts.timeout();
                }
            });
            triggers.gameStarted({});
        });
    }
}

function highlightDesign() {
    initMap();
    var path = createLinearPath(db.path);
    $(path).attr("class", "highlight");
    $("#front").append(path);

    $("line").each(function () {
        var x1 = parseFloat($(this).attr("x1"));
        var y1 = parseFloat($(this).attr("y1"));
        var x2 = parseFloat($(this).attr("x2"));
        var y2 = parseFloat($(this).attr("y2"));
        path = createLinearPath([{ x: x1, y: y1 }, { x: x2, y: y2}]);
        $(path).attr("class", "highlight");
        $("#main").append(path);
    });
}

var geo = {
    p2lineProject: function (p, p1, p2) {
        var dir = geo.vectorDiff(p2, p1);
        var diff = geo.vectorDiff(p, p1);
        var t = geo.vectorDot(diff, dir) / geo.vectorDot(dir, dir);
        return geo.vectorAdd(p1, geo.vectorProcuctNum(dir, t));
    },
    angle3p: function (pm, p1, p2) {
        var v1 = { x: p1.x - pm.x, y: p1.y - pm.y };
        var v2 = { x: p2.x - pm.x, y: p2.y - pm.y };

        return geo.angle2v(v1, v2);
    },
    angle3p2: function (pm, p1, p2) {
        var dism1 = geo.dist2p(pm, p1),
        dism2 = geo.dist2p(pm, p2),
        dis12 = geo.dist2p(p1, p2);


        return Math.acos((dism1 * dism1 + dism2 * dism2 - dis12 * dis12) / (2 * dism1 * dism2)) * 180 / Math.PI;
    },
    angle2v: function (v1, v2) {
        var dis1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
        var dis2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);

        var angle = (v1.x * v2.y - v2.x * v1.y) / (dis1 * dis2);

        return Math.asin(angle) * 180 / Math.PI;
    },
    dist2p: function (p1, p2) {
        var x = p1.x - p2.x;
        var y = p1.y - p2.y;
        return Math.sqrt(x * x + y * y);
    },
    distP2Line: function (A, B, P) {
        var normalLength = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
        return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / normalLength;
    },
    lineSegIntersec: function (A, B, C, D) {
        function ccw(A, B, C) {
            return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
        }

        return ccw(A, C, D) != ccw(B, C, D) && ccw(A, B, C) != ccw(A, B, D);
    },
    vectorLen: function (v) {
        return Math.sqrt(v.x * v.x + v.y * v.y);
    },
    vectorDir: function (v) {
        if (v.x === 0 && v.y === 0)
            return null;
        if (v.y === 0) {
            return v.x > 0 ? 90 : -90;
        }
        if (v.x == 0) {
            return v.y > 0 ? 180 : 0;
        }
        var angle = Math.atan(v.x / v.y * -1) * 180 / Math.PI;
        if (v.y < 0)
            return angle;
        else
            return angle + 180;
    },
    vectorNorm: function (v) {
        var length = Math.sqrt(v.x * v.x + v.y * v.y);
        return { x: v.x / length, y: v.y / length };
    },
    vectorAdd: function (v1, v2) {
        return { x: v1.x + v2.x, y: v1.y + v2.y };
    },
    vectorDiff: function (v1, v2) {
        return { x: v1.x - v2.x, y: v1.y - v2.y };
    },
    vectorDot: function (v1, v2) {
        return v1.x * v2.x + v1.y * v2.y;
    },
    vectorCross: function (v1, v2) {
        return v1.x * v2.y - v2.x * v1.y;
    },
    vectorProcuctNum: function (v, num) {
        return { x: v.x * num, y: v.y * num };
    },
    vectorProject: function (v1, v2) {
        var l2 = geo.vectorLen(v2);
        if (l2 > 0) {
            return geo.vectorProcuctNum(v2, geo.vectorDot(v1, v2) / (l2 * l2));
        }
        return { x: 0, y: 0 };
    },
    vectorPerpLeft: function (v) {
        return { x: v.y, y: -v.x };
    },
    vectorPerpRight: function (v) {
        return { x: -v.y, y: v.x };
    },
    circleLineIntersec: function (center, radius, x1, y1, x2, y2, infLeng) {
        var d = { x: x2 - x1, y: y2 - y1 };
        var f = { x: x1 - center.x, y: y1 - center.y };

        var a = d.x * d.x + d.y * d.y;
        var b = 2 * (f.x * d.x + f.y * d.y); //f.Dot( d ) ;
        var c = f.x * f.x + f.y * f.y - radius * radius; //f.Dot(f) - r * r;

        var discriminant = b * b - 4 * a * c;
        var p1 = null, p2 = null;
        if (discriminant < 0) {
            // no intersection
            //return null;
        }
        else {
            // ray didn't totally miss sphere,
            // so there is a solution to
            // the equation.


            discriminant = Math.sqrt(discriminant);
            // either solution may be on or off the ray so need to test both
            var t1 = (-b + discriminant) / (2 * a);
            if (infLeng || (t1 >= 0 && t1 <= 1)) {
                p1 = {
                    x: x1 + t1 * (x2 - x1),
                    y: y1 + t1 * (y2 - y1)
                };
            }
            if (discriminant > 0) {
                var t2 = (-b - discriminant) / (2 * a);

                if (infLeng || t2 >= 0 && t2 <= 1) {
                    p2 = {
                        x: x1 + t2 * (x2 - x1),
                        y: y1 + t2 * (y2 - y1)
                    };
                }
            }
        }


        return { p1: p1, p2: p2 };
    },

    pInCircle: function (center, radius, x, y) {
        return geo.dist2p(center, { x: x, y: y }) <= radius;
    }
};
geo.__namespace = true;

var tip = {
    hit: function (x, y, opts) {
        opts = $.extend({
            color: "#FF2C21",
            time: 0.4,
            container: $("#front")
        }, opts || {});
        var hit = convertStringToSvg('<circle r="1" cx="' + x + '" cy="' + y + '" fill="' + opts.color + '" fill-opacity="0.7" stroke="' + opts.color + '" stroke-width="3"/>');
        var r = 1;
        var opacity = 1;
        db.setTimer(opts.time,
        function () {
            r += 0.8;
            hit.attr("r", r);
            opacity -= 0.03;
            hit.attr("opacity", opacity);
        },
        function () {
            hit.remove();
        });

        opts.container.append(hit);
    }
};
tip.__namespace = true;

var sound = {
    once: function (sound) {
        $("body").append("<embed src='../sound/" + sound + "' hidden='true' autostart='true' loop='false'/>");
    }
};
sound.__namespace = true;

function getPosition(e, ignoreScroll) {
    var posx = 0;
    var posy = 0;

    if (e.originalEvent && (e.originalEvent.touches || e.originalEvent.changedTouches)) {
        var touches = e.originalEvent.touches || e.originalEvent.changedTouches;

        posx = touches[0].pageX;
        posy = touches[0].pageY;
    }

    else {
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft
    			+ document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop
    			+ document.documentElement.scrollTop;
        }
    }
    if (ignoreScroll)
        return { x: posx - db.offset.left, y: posy - db.offset.top };
    else
        return { x: posx + $("#win").scrollLeft() - db.offset.left, y: posy + $("#win").scrollTop() - db.offset.top };
}

Ball.__class = true;
function Ball(opts) {
    this.radius = 40;
    this.resistance = 0.9;
    this.gravity = 1.2;
    this.speed_a = 0.1;
    this.air_resistance = 0.5;
    this.DOM = null;
    this.isActive = false;

    opts = $.extend({
        hit: function (sender, e) { }
    }, opts || {});

    var speed = { x: 0, y: 0 };
    var currentPosition = { x: 0, y: 0 };
    var previousPosition = null;

    var $self = this;

    this.init = function (x, y) {
        $self.DOM = convertStringToSvg('<g db-cat="ball">' +
                '<defs>' +
              '<radialGradient id="radialGradient3785" cx="35%"  cy="50%"  fx="15%"  fy="5%"  r="60%" >' +
                '<stop style="stop-color:#ffd52a;stop-opacity:1;"  offset="0%" />' +
                '<stop style="stop-color:#ff7f2a;stop-opacity:1;" offset="100%"/>' +
              '</radialGradient>' +
            '</defs>' +
            '<g transform="scale(0.32)">' +
              '<path style="fill:url(#radialGradient3785);fill-rule:nonzero;stroke:#000000;stroke-width:4.92430829999999990;fill-opacity:1"  d="m-109.92151,55.00543c-30.11295,-61.16013 -4.83795,-135.19882 56.41739,-165.26496c61.25534,-30.06615 135.4093,-4.83044 165.52225,56.32969c30.11292,61.16013 4.83795,135.19882 -56.41739,165.26498c-61.19952,30.03876 -135.27103,4.89014 -165.44408,-56.1712"/>' +
              '<path style="fill:none;stroke:#000000;stroke-width:4.39474154;" d="m-121.33701,-11.80801c19.44699,-47.327 235.47,-0.823 244.77,24.69202"/>' +
              '<path style="fill:none;stroke:#000000;stroke-width:4.39474154;" d="m30.85193,-120.45459c103.99561,36.62695 55.80252,231.28479 -41.8519,243.21942"/>' +
              '<path style="fill:none;stroke:#000000;stroke-width:4.39474154;" d="m-50.3154,-111.81229c59.18451,4.93848 98.07718,93.83084 161.06641,61.73083"/>' +
              '<path style="fill:none;stroke:#000000;stroke-width:4.39474154;" d="m-104.8497,65.97247c84.97205,12.75769 197.84537,-105.35394 205.4548,8.64233"/>' +
            '</g>' +
        +'</g>');
        $self.DOM.setTranslate(x, y);
        $self.DOM.bind('active', function (e, active) {
            activateBall(active);
        })

        $self.DOM.prepend(convertStringToSvg('<circle class="back" r="43" fill="red" visibility="hidden" ></circle>'));
        currentPosition = { x: x, y: y };

        var target = $("#main [db-cat='target']");
        if (target.length > 0)
            target.before($self.DOM);
        else
            $("#main").append($self.DOM);

        function push(e) {
            if ($self.isActive && !db.isEnded && !db.isTimeout) {

                var mousePosi = getPosition(e);
                tip.hit(mousePosi.x, mousePosi.y);

                var diff = geo.vectorDiff(mousePosi, currentPosition);
                speed = geo.vectorAdd(speed, geo.vectorProcuctNum(diff, $self.speed_a));
                activateBall(false);
                triggers.clicked({ position: mousePosi });
            }
        }

        $('#dummy').mousedown(function (e) {
            push(e);
        });

        $('#dummy').bind("touchstart", function (e) {
            e.preventDefault();
            push(e);
        });

        var checkGameOver = false;
        db.setTimer(null, function () {
            //move
            if (speed.x != 0 || speed.y != 0) {
                currentPosition = {
                    x: currentPosition.x + speed.x,
                    y: currentPosition.y + speed.y
                };
                triggers.ballMoved({ position: currentPosition });
                db.setLights('ball', 80, currentPosition);
                if (db.isInBoundary(currentPosition.x, currentPosition.y)) {
                    $self.DOM.setTranslate(currentPosition.x, currentPosition.y);
                    camera.set(currentPosition.x, currentPosition.y, true);
                }
                else {
                    db.gameEnded(false);
                    return true;
                }
            }
            else {
                camera.set(currentPosition.x, currentPosition.y, false);
            }

            //gravtity
            speed = geo.vectorAdd(speed, { x: 0, y: $self.gravity });

            //paddles
            var paddleForce = { x: 0, y: 0 };
            var paddleMove = { x: 0, y: 0 };
            var hitPaddles = false;
            $("#main").find('[db-pad]').each(function () {
                if ($(this).is("line")) {
                    var line = false;
                    var point = null;

                    var x1 = parseFloat($(this).attr("x1"));
                    var y1 = parseFloat($(this).attr("y1"));
                    var x2 = parseFloat($(this).attr("x2"));
                    var y2 = parseFloat($(this).attr("y2"));

                    var p1In = geo.pInCircle(currentPosition, $self.radius, x1, y1);
                    var p2In = geo.pInCircle(currentPosition, $self.radius, x2, y2);

                    if (p1In && p2In) {
                        line = true;
                    }

                    else {
                        var intersections = geo.circleLineIntersec(currentPosition, $self.radius, x1, y1, x2, y2, false);
                        if (intersections.p1 && intersections.p2) {
                            line = true;
                        }
                        else if (intersections.p1 || intersections.p2) {
                            var pInter = intersections.p1 || intersections.p2;
                            var pIn = p1In ? { x: x1, y: y1} : { x: x2, y: y2 };

                            if (Math.abs(geo.angle3p2(pIn, pInter, currentPosition)) <= 90) {
                                line = true;
                            }
                            else {
                                point = pIn;
                            }
                        }
                    }

                    if (line || point != null) {
                        var pad = $(this).attr("db-pad");
                        if (pad == "bad") {
                            db.gameEnded(false);
                        }

                        var speedX = parseFloat($(this).attr("db-speed-x"));
                        var speedY = parseFloat($(this).attr("db-speed-y"));
                        var res = parseFloat($(this).attr("db-res"));

                        if (line) {
                            if ($(this).attr("db-bi") == "1") {
                                if (geo.angle3p({ x: x1, y: y1 }, { x: x2, y: y2 }, currentPosition) < 0)
                                    var forceDirect = geo.vectorPerpLeft({ x: x2 - x1, y: y2 - y1 });
                                else
                                    forceDirect = geo.vectorPerpRight({ x: x2 - x1, y: y2 - y1 });
                            }
                            else
                                forceDirect = geo.vectorPerpLeft({ x: x2 - x1, y: y2 - y1 });
                        }
                        else {
                            var normForceDirect = geo.vectorPerpLeft({ x: x2 - x1, y: y2 - y1 });
                            forceDirect = geo.vectorDiff(currentPosition, point);

                            if (normForceDirect.x * forceDirect.x < 0)
                                forceDirect.x = 0;
                            if (normForceDirect.y * forceDirect.y < 0)
                                forceDirect.y = 0;
                        }
                        if ($(this).attr("db-hp")) {
                            var force = calSpeed({ x: speedX, y: speedY }, forceDirect, res * $self.resistance, parseFloat($(this).attr("db-hp")));
                            updatePaddleLayout($(this), force);
                        }
                        else
                            force = calSpeed({ x: speedX, y: speedY }, forceDirect, res * $self.resistance);

                        opts.hit($(this), { force: force });

                        paddleForce = geo.vectorAdd(paddleForce, force);

                        paddleMove = geo.vectorAdd(paddleMove, { x: speedX, y: speedY });
                        if (pad != "border" && pad != "bad")
                            hitPaddles = true;
                    }
                }
                else if ($(this).is("circle")) {
                    var cx = parseFloat($(this).attr("cx"));
                    var cy = parseFloat($(this).attr("cy"));
                    var r = parseFloat($(this).attr("r"));
                    if (geo.dist2p({ x: cx, y: cy }, currentPosition) < r + $self.radius) {
                        pad = $(this).attr("db-pad");
                        if (pad == "bad") {
                            db.gameEnded(false);
                        }

                        speedX = parseFloat($(this).attr("db-speed-x"));
                        speedY = parseFloat($(this).attr("db-speed-y"));
                        res = parseFloat($(this).attr("db-res"));
                        forceDirect = geo.vectorDiff(currentPosition, { x: cx, y: cy });

                        if ($(this).attr("db-hp")) {
                            force = calSpeed({ x: speedX, y: speedY }, forceDirect, res * $self.resistance, parseFloat($(this).attr("db-hp")));
                            updatePaddleLayout($(this), force);
                        }
                        else
                            force = calSpeed({ x: speedX, y: speedY }, forceDirect, res * $self.resistance);

                        opts.hit($(this), { force: force });

                        paddleForce = geo.vectorAdd(paddleForce, force);
                        paddleMove = geo.vectorAdd(paddleMove, { x: speedX, y: speedY });
                        if (pad != "border" && pad != "bad")
                            hitPaddles = true;
                    }
                }
            });


            if (!db.isEnded) {
                //button
                $("#main").find("[db-button]").each(function () {
                    var on = $(this).attr("db-button-on");
                    if (on == null) {
                        var buttonP = $(this).getTranslate();

                        var intersections = geo.circleLineIntersec(currentPosition, $self.radius, buttonP.x - 35, buttonP.y, buttonP.x + 35, buttonP.y, false);
                        if (intersections.p1 || intersections.p2) {
                            $(this).attr("db-button-on", "1");
                            $(this).find("rect").attr({ "height": 5, "y": 2.5 });
                            triggers.hitButton({ sender: $(this) });
                        }
                    }
                });
                //toggle
                $("#main").find("[db-toggle]").each(function () {
                    var buttonP = $(this).getTranslate();

                    var intersections = geo.circleLineIntersec(currentPosition, $self.radius, buttonP.x - 35, buttonP.y, buttonP.x + 35, buttonP.y, false);
                    if (intersections.p1 || intersections.p2) {
                        var isOn = $(this).attr("db-toggle-on");
                        if (isOn == "1")
                            $(this).attr("db-toggle-on", "0");
                        else
                            $(this).attr("db-toggle-on", "1");
                        triggers.hitToggle({ id: $(this).attr("db-toggle"), isOn: isOn != "1" });
                    }
                });
                //reward
                $("#main").find('[db-cat="reward"]').each(function () {
                    var rp = $(this).getTranslate()
                    if (geo.dist2p(currentPosition, rp) < 30 + $self.radius) {
                        sound.once("reward.wav");
                        var moved = true;
                        $(this).find(".move").each(function () {
                            if (this.beginElement) {
                                this.beginElement();
                            }
                            else {
                                moved = false;
                            }
                        });
                        if (!moved)
                            $(this).animate({ opacity: 0 }, 1200);

                        $(this).removeAttr("db-cat");
                        triggers.getReward({ sender: $(this) });
                    }
                });

                //target
                if (previousPosition != null) {
                    $("#main").find('[db-cat="target"]').each(function () {
                        var x1 = parseFloat($(this).attr("x1"));
                        var y1 = parseFloat($(this).attr("y1"));
                        var x2 = parseFloat($(this).attr("x2"));
                        var y2 = parseFloat($(this).attr("y2"));

                        var p1 = { x: x1, y: y1 };
                        var p2 = { x: x2, y: y2 };

                        if (geo.lineSegIntersec(currentPosition, previousPosition, p1, p2)) {
                            if (geo.angle2v(geo.vectorDiff(currentPosition, previousPosition), geo.vectorDiff(p2, p1)) < 90) {
                                db.gameEnded(true);
                            }
                        }
                    });
                }
            }

            setSpeedR();
            if (hitPaddles) {
                activateBall(true);
                if (paddleForce.x != 0 || paddleForce.y != 0) {

                    //adjast gravity
                    if (paddleForce.y < 0 && (speed.y < 0 && speed.y > -$self.gravity))
                        speed.y = 0;

                    //adjast paddle movtion
                    if (paddleMove.x != 0 || paddleMove.y != 0) {
                        if (paddleMove.x * paddleForce.x > 0 && (paddleMove.x * speed.x < 0 || Math.abs(paddleMove.x) > Math.abs(speed.x))) {
                            speed.x = paddleMove.x;
                        }
                        if (paddleMove.y * paddleForce.y > 0 && (paddleMove.y * speed.y < 0 || Math.abs(paddleMove.y) > Math.abs(speed.y))) {
                            speed.y = paddleMove.y;
                        }
                    }
                    if (geo.vectorLen(paddleForce) > 5) {
                        //sound.once("hit.mp3");
                    }
                }
            }
            if (db.isTimeout) {
                if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
                    if (checkGameOver) {
                        db.gameEnded(false);
                    }
                    else
                        checkGameOver = true;
                }
                else
                    checkGameOver = false;
            }
            //log
            previousPosition = currentPosition;
        }, function () { });
    }

    function calSpeed(objectSpeed, forceDir, a, max) {
        var relativeSpeed = geo.vectorDiff(objectSpeed, speed);

        if (relativeSpeed.x * forceDir.x <= 0)
            relativeSpeed.x = 0;
        if (relativeSpeed.y * forceDir.y <= 0)
            relativeSpeed.y = 0;

        var force = geo.vectorProcuctNum(geo.vectorProject(relativeSpeed, forceDir), 1 + a);

        if (force.x != 0 || force.y != 0) {
            if (max == undefined || geo.vectorLen(force) < max) {
                speed = geo.vectorAdd(speed, force);
            }
            else {
                force = geo.vectorProcuctNum(geo.vectorNorm(force), max);
                speed = geo.vectorAdd(speed, force);
            }
        }

        return force;
    }

    function setSpeedR(resistance) {
        if (resistance == undefined)
            resistance = $self.air_resistance;
        if (speed.x != 0 || speed.y != 0) {
            var r = geo.vectorProcuctNum(geo.vectorNorm(speed), resistance);

            if (Math.abs(r.x) < Math.abs(speed.x))
                speed.x -= r.x;
            else {
                speed.x = 0;
            }

            if (Math.abs(r.y) < Math.abs(speed.y))
                speed.y -= r.y;
            else {
                speed.y = 0;
            }
        }
    }

    function activateBall(active) {
        if (active) {
            if (!db.isEnded && !db.isTimeout) {
                $self.DOM.find("circle[class='back']").attr("visibility", "visible");
                $self.isActive = true;
            }
        }
        else {
            $self.DOM.find("circle[class='back']").attr("visibility", "hidden");
            $self.isActive = false;
        }

    }
}

function initPaddle(dom) {
    dom.attr("db-cat", "pad")
    dom.attr("db-speed-x", "0")
    dom.attr("db-speed-y", "0");

    var type = dom.attr("db-pad");
    switch (type) {
        case "bad":
            dom.attr("db-res", 0);
            break;
        case "border":
            dom.attr("db-res", 0);
            break;
        case "wood":
            dom.attr("db-res", 0.5);
            break;
        case "ice":
            dom.attr("db-res", 1);
            dom.attr("db-hp-max", 100);
            dom.attr("db-hp", 100);
            break;
        case "glass":
            dom.attr("db-res", 1);
            dom.attr("db-hp-max", 30);
            dom.attr("db-hp", 30);
            break;
        case "ground":
        default:
            dom.attr("db-res", 1);
            break;
    }
}

function initMap(opts) {
    opts = $.extend({
        addCeilingBorder: true
    }, opts || {});

    if (opts.addCeilingBorder) {
        var w = $("svg").attr("width");
        $("#main").append(convertStringToSvg('<line x2="0" y2="0" x1="' + w + '" y1="0" db-pad="border" />'));
    }

    $("#main").find('[db-pad]').each(function () {
        initPaddle($(this));
    });

    $("#main").find("[db-cat='target']").each(function () {
        var x1 = $(this).attr("x1");
        var y1 = parseFloat($(this).attr("y1"));
        $("#main").append(convertStringToSvg('<image class="target" href="../img/hoop.gif"  x="' + x1 + '" y="' + (y1 - 30) + '" height="160" width="160"></image>'));
    });


    $("svg").append(convertStringToSvg('<defs><linearGradient id="linearGradient3223" x1="80%" y1="80%" x2="30%"  y2="30%"><stop style="stop-color:#fca72a;" offset="0" /><stop style="stop-color:#fbfc3e;" offset="1" /></linearGradient></defs>'));
   
    $("#main").find("[db-cat='reward']").each(function () {
        $(this).append(convertStringToSvg('<g><path style="fill:url(#linearGradient3223);stroke:#f57900;stroke-width:2.5;opacity:1" d="m0.45239,-20.21191l-6.7622,13.79604c-0.06201,0.13186 -0.16972,0.23694 -0.30328,0.29585c-0.03632,0.01804 -0.07438,0.03244 -0.11361,0.04291l-15.12898,2.16442l10.97959,10.64492c0.14671,0.147 0.20752,0.35875 0.16107,0.56087l-2.5753,15.03845l13.56019,-7.11545c0.17184,-0.08609 0.37447,-0.0857 0.54597,0.00105l13.5271,7.09245l-2.60854,-15.07544c-0.03422,-0.19192 0.02652,-0.3885 0.1632,-0.52791l10.97792,-10.69942l-15.12601,-2.13677c-0.19595,-0.0253 -0.36722,-0.14434 -0.45885,-0.3189l-6.83825,-13.76309l-0.00002,0l0,0.00001l0,0.00001z"/>' +
                '<animateTransform id="c1" attributeName="transform" attributeType="XML" type="scale" values="1;1.1;1" dur="1s" fill="freeze" repeatCount="indefinite"/>' +
                 '<animate attributeType="css" attributeName="opacity" values="0.9;1;0.9" dur="1s" fill="freeze" repeatCount="indefinite"/>' +
                 '<animateMotion class="move" path="m0,0c522,-170 -77,-231 141,-378" begin="indefinite" dur="1.5s" fill="freeze"/>' +
                 '<animate class="move" attributeType="css" attributeName="opacity" from="1" to="0" dur="1.5s" fill="freeze" repeatCount="1" begin="indefinite"/>' +
                 '</g>'));
    });

    $("#main").find("[db-button]").each(function () {
        $(this).append(convertStringToSvg('<rect x="-35" y="-10" width="70" height="20" fill="url(#button)" />'));
    });

    $("#light circle").each(function () {
        if ($(this).attr("id") != "ball") {
            var x = parseFloat($(this).attr("cx"));
            var y = parseFloat($(this).attr("cy"));
            //            $("#main").append(convertStringToSvg('<g transform="translate(' + x + ' ' + y + ') scale(0.2)"><defs>' +
            //      '<radialGradient id="radialGradient3507" cx="30%" cy="30%" fx="30%" fy="30%" r="100%">' +
            //        '<stop style="stop-color:#000000;stop-opacity:0;" offset="0"/>' +
            //        '<stop style="stop-color:#000000;stop-opacity:0.53125;" offset="1"/>' +
            //      '</radialGradient>' +
            //      '<radialGradient id="radialGradient3509" cx="30%" cy="30%" fx="30%" fy="30%" r="100%">' +
            //        '<stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" />' +
            //        '<stop style="stop-color:#ffffff;stop-opacity:0.6640625;" offset="0.65625" />' +
            //        '<stop style="stop-color:#ffffff;stop-opacity:0.3359375;" offset="1" />' +
            //      '</radialGradient>' +
            //    '</defs>' +
            //    '<path fill="#ffff02" fill-rule="evenodd" stroke="#000000" stroke-width="1.25" stroke-miterlimit="0" stroke-dashoffset="0" d="m-0.46875,-99.12619c-40.14822,1.01587 -71.99893,27.04916 -72.46876,74.16763c-0.46975,47.11075 37.40107,37.25806 38.40626,104.18213c0.00699,0.44806 35.50452,-0.40356 71.66895,-0.0152c1.31415,-66.5498 38.64355,-57.64801 38.64355,-104.16693c0,-46.52167 -33.31534,-74.16763 -74.34375,-74.16763m0,0c-0.60318,0 -1.23108,-0.01709 -1.90625,0"/>' +
            //    '<rect fill="#b8b8b8" fill-rule="evenodd" stroke="#000000" stroke-width="1.25" stroke-miterlimit="0" stroke-dashoffset="0" width="62.12438" height="38.38579" x="-29.64249" y="79.00165"/>' +
            //    '<path fill="none" stroke="#626262" stroke-width="2.5" stroke-miterlimit="4" d="m-29.2334,80.00165l61.61933,5.55585"/>' +
            //    '<path fill="none" stroke="#626262" stroke-width="2.5" stroke-miterlimit="4" d="m-29.2334,88.00165l61.61933,5.55585"/>' +
            //    '<path fill="none" stroke="#626262" stroke-width="2.5" stroke-miterlimit="4" d="m-29.2334,96.00165l61.61933,5.55585"/>' +
            //    '<path fill="none" stroke="#626262" stroke-width="2.5" stroke-miterlimit="4" d="m-29.2334,111.00165l61.61933,5.55585"/>' +
            //    '<path fill="none" stroke="#626262" stroke-width="2.5" stroke-miterlimit="4" d="m-29.2334,104.00165l61.61933,5.55585"/>' +
            //    '<rect fill="#b8b8b8" fill-rule="evenodd" stroke="#000000" stroke-width="1.25" stroke-miterlimit="0" stroke-dashoffset="0" width="15.72835" height="9.71833" x="-6.44446" y="120.58789"/>' +
            //    '<path fill="#282828" fill-rule="evenodd" d="m-29.96164,118.20276l62.84354,-0.12628l-8.58627,9.28571l-46.42859,0l-7.82867,-9.15942z"/>' +
            //    '<path opacity="0.83051" fill="url(#radialGradient3507)" fill-rule="evenodd" d="m-0.46875,-99.12619c-40.14822,1.01587 -71.99893,27.04916 -72.46876,74.16763c-0.46975,47.11075 37.40107,37.25806 38.40626,104.18213c0.00699,0.44806 35.50452,-0.40356 71.66895,-0.0152c1.31415,-66.5498 38.64355,-57.64801 38.64355,-104.16693c0,-46.52167 -33.31534,-74.16763 -74.34375,-74.16763m0,0c-0.60318,0 -1.23108,-0.01709 -1.90625,0"/>' +
            //    '<path fill="url(#radialGradient3509)" fill-rule="evenodd" d="m-0.46875,-99.12619c-40.14822,1.01587 -71.99893,27.04916 -72.46876,74.16763c-0.46975,47.11075 37.40107,37.25806 38.40626,104.18213c0.00699,0.44806 35.50452,-0.40356 71.66895,-0.0152c1.31415,-66.5498 38.64355,-57.64801 38.64355,-104.16693c0,-46.52167 -33.31534,-74.16763 -74.34375,-74.16763m0,0c-0.60318,0 -1.23108,-0.01709 -1.90625,0"/></g>'));
            $("#main").append(convertStringToSvg('<image href="../img/bulb.png"  x="' + (x - 35) + '" y="' + (y - 35) + '" height="70" width="70"></image>'));
        }
    });
}

function updatePaddleLayout(paddle, force) {
    var hp = parseFloat(paddle.attr("db-hp"));
    var f = geo.vectorLen(force);
    hp -= f;
    if (hp < 1) {
        paddle.remove();
        triggers.padRemoved({ pad: paddle });
    }
    else {
        var hp_max = parseFloat(paddle.attr("db-hp-max"));
        paddle.attr("db-hp", hp);
        switch (paddle.attr("db-pad")) {
            case "ice":
            case "glass":
                paddle.attr("stroke-dasharray", (hp / 10) + " " + ((hp_max - hp) / 10));
                break;
        }
    }
}